﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GpuStructCompiler
{
    public class Parser
    {
        private Token[] tokens;
        public Document Parse(IEnumerable<Token> tokens)
        {
            this.tokens = tokens.ToArray();
            Document doc = new Document();
            int ptr = 0;
            while (true)
            {
                StructDeclare structDeclare = ParseStructDeclare(ref ptr);
                if (structDeclare == null)
                    break;
                else
                    doc.Structs.Add(structDeclare);
            }
            return doc;
        }

        private Token ReadKeyword(ref int ptr, string keyword)
        {
            Token tk = ReadToken(ref ptr, TokenType.Identifier);
            if (tk == null)
                return null;
            if (tk.Text == null)
                return null;
            return tk;
        }

        private Token ReadToken(ref int ptr, TokenType t)
        {
            if (ptr < tokens.Length)
            {
                if (tokens[ptr].TokenType == t)
                    return tokens[ptr++];
            }
            return null;
        }

        private StructDeclare ParseStructDeclare(ref int ptr)
        {
            try
            {
                ReadKeyword(ref ptr, "struct");
                StructDeclare rs = new StructDeclare();
                rs.Name = ReadToken(ref ptr, TokenType.Identifier).Text;
                ReadToken(ref ptr, TokenType.LBrace);
                while (tokens[ptr].TokenType != TokenType.RBrace)
                {
                    rs.Members.AddRange(ParseVariableDeclarations(ref ptr));
                }
                ReadToken(ref ptr, TokenType.RBrace);
                ReadToken(ref ptr, TokenType.Semicolon);
                return rs;
            }
            catch
            {
                return null;
            }
        }

        private IEnumerable<VariableDeclare> ParseVariableDeclarations(ref int ptr)
        {
            List<VariableDeclare> rs = new List<VariableDeclare>();
            while (ptr < tokens.Length && tokens[ptr].TokenType == TokenType.Identifier)
            {
                VariableDeclare vd = new VariableDeclare();
                vd.TypeName = ReadToken(ref ptr, TokenType.Identifier).Text;
                if (vd.TypeName == "struct")
                    vd.TypeName = ReadToken(ref ptr, TokenType.Identifier).Text;
                vd.Name = ReadToken(ref ptr, TokenType.Identifier).Text;
                rs.Add(vd);
                while (ptr < tokens.Length && tokens[ptr].TokenType == TokenType.Comma)
                {
                    ReadToken(ref ptr, TokenType.Comma);
                    VariableDeclare v = new VariableDeclare();
                    v.TypeName = vd.TypeName;
                    v.Name = ReadToken(ref ptr, TokenType.Identifier).Text;
                    rs.Add(v);
                }
                ReadToken(ref ptr, TokenType.Semicolon);
            }
            return rs;
        }
    }
}
